package io.openmessaging.utils;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 统计数据
 *
 * @author cuijd    2021/9/28 13:57
 */
public class Statistic {
    public interface Append {
        ThreadLocalLongAdder count = new ThreadLocalLongAdder();
        ThreadLocalLongAdder dataSize = new ThreadLocalLongAdder();
//        ConcurrentHashMap<Thread, Boolean> threads = new ConcurrentHashMap<>();

        static void add(long size) {
            if (!logStarted)
                return;
//            threads.putIfAbsent(Thread.currentThread(), Boolean.TRUE);
            count.add(1);
            dataSize.add(size);
        }
    }

    public interface SSD {
        ConcurrentHashMap<Thread, AtomicInteger> writeCount = new ConcurrentHashMap<>();
        ConcurrentHashMap<Thread, AtomicInteger> forceCount = new ConcurrentHashMap<>();
        ThreadLocalLongAdder readCount = new ThreadLocalLongAdder();
        ThreadLocalLongAdder readSize = new ThreadLocalLongAdder();

        static void force(int writeCount_) {
            if (!logStarted)
                return;
            writeCount.computeIfAbsent(Thread.currentThread(), k -> new AtomicInteger()).getAndAdd(writeCount_);
            forceCount.computeIfAbsent(Thread.currentThread(), k -> new AtomicInteger()).getAndAdd(1);
        }

        static void read(int size) {
            if (!logStarted)
                return;
            readCount.add(1);
            readSize.add(size);
        }
    }

    public interface Pmem {
        ThreadLocalLongAdder writeSize = new ThreadLocalLongAdder();//pmem写入数据量
        ThreadLocalLongAdder readSize = new ThreadLocalLongAdder();//pmem命中数据量
        ThreadLocalLongAdder freeSize = new ThreadLocalLongAdder();//释放大小
        AtomicLong saveQueuesSize = new AtomicLong();//保存队列中的数据大小

        static void saveQueuesSizeAdd(long size) {
            if (!logStarted)
                return;
            saveQueuesSize.getAndAdd(size);
        }

        static void write(long size) {
            if (!logStarted)
                return;
            writeSize.add(size);
        }

        static void read(long size) {
            if (!logStarted)
                return;
            readSize.add(size);
        }

        static void free(long size) {
            if (!logStarted)
                return;
            freeSize.add(size);
        }
    }

    private static boolean logStarted = false;

    public static void startLog() {
        logStarted = true;
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        OperatingSystemMXBean sys = ManagementFactory.getOperatingSystemMXBean();
        Thread logThread = new Thread(() -> {
            try {
                int loggingInterval = 5;//打印日志时间间隔
                while (true) {
                    TimeUnit.SECONDS.sleep(loggingInterval);

                    Log.i("Append消息数: ", Strings.leftPad(Append.count, 9)
                            , "  数据量: ", Strings.format(Append.dataSize.longValue())
                            , "  速度: 【", Strings.format(Append.dataSize.increments() / loggingInterval, "b/s"), "】"
//                            ," 线程数：", Append.threads.size()
                    );

                    StringBuilder sb = new StringBuilder();
                    SSD.writeCount.forEach((thread, wCount) ->
                            sb.append(thread.getName()).append(":")
                                    .append(Strings.format(SSD.forceCount.get(thread), ""))
                                    .append("/").append(Strings.format(wCount, "")).append("  ")
                    );
                    if (SSD.readCount.longValue() > 0)
                        Log.i("SSD"
//                            , "(force/write) : ", sb
                                , " read:", SSD.readCount, "个 ", Strings.format(SSD.readSize)
                                , " 速度：【", Strings.format(SSD.readSize.increments() / loggingInterval, "b/s"), "】"
//                            ,"统计耗时：", SSD.statisticCost.cost()
                        );

                    Log.i("Pmem: "
                            , " pmem写入: ", Strings.format(Pmem.writeSize)
                            , " pmem命中: ", Strings.format(Pmem.readSize)
                            , " pmem释放: ", Strings.format(Pmem.freeSize)
                            , " save队列数据量: ", Strings.format(Pmem.saveQueuesSize)
                    );


                    MemoryUsage heap = memoryMXBean.getHeapMemoryUsage();
                    MemoryUsage nonHeap = memoryMXBean.getNonHeapMemoryUsage();
                    Log.i("堆内存：", Strings.format(heap.getUsed()), "/", Strings.format(heap.getMax())
                            , " 堆外内存：", Strings.format(nonHeap.getUsed()), "/", Strings.format(nonHeap.getMax())
                            , " 系统负载：", sys.getSystemLoadAverage(), " ", sys.getAvailableProcessors()
                    );
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        logThread.setName("Thread-Log");
        logThread.setPriority(Thread.MIN_PRIORITY);
        logThread.start();
    }
}
